
#include "nane/core/crt/Backtrace_.h"
#include "nane/core/WinAPI/winapi.h"

#include <bfd.h>
#include <cxxabi.h>

#include <cassert>
#include <cstdlib>
#include <cstring>

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    struct StackframeFindData
    {
        asymbol** symbolTable;
        bfd_vma address;
        char* text;
    };
    static void s_findFunctionNameInSection( bfd* _hBfd, asection* _section, void* _userData )
    {
        assert(_section);
        assert(_userData);
        StackframeFindData* data = static_cast<StackframeFindData*>(_userData);

        if( data->text != NULL )
        {
            return; // already found it
        }

        if( (bfd_get_section_flags(_hBfd, _section) & SEC_ALLOC) == 0 )
        {
            return;
        }

        bfd_vma vma = bfd_get_section_vma(_hBfd, _section);
        if( data->address < vma || vma + bfd_get_section_size(_section) <= data->address )
        {
            return;
        }

        const char *func = 0;
        const char *file = 0;
        unsigned line = 0;

        if( bfd_find_nearest_line(_hBfd, _section, data->symbolTable, data->address - vma, &file, &func, &line) && func )
        {
            int status = 0;
            data->text = abi::__cxa_demangle(func, 0, 0, &status);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Win32BacktraceGetSymbolsImpl( StackFrame* _stackFrames, USHORT _count )
    {
        char procname[MAX_PATH];
        ::GetModuleFileNameA(NULL, procname, MAX_PATH);

        bfd_init();
        bfd* hBfd = bfd_openr(procname, NULL);
        if( hBfd != NULL 
            && bfd_check_format(hBfd, bfd_object) == TRUE 
            && (bfd_get_file_flags(hBfd) & HAS_SYMS) != 0 )
        {
            char** formats = 0;
            if( bfd_check_format_matches(hBfd, bfd_object, &formats) == TRUE )
            {
                free(formats);
                
                // Load symbol table
                asymbol** symbolTable;
                unsigned dummy = 0;
                if( bfd_read_minisymbols(hBfd, FALSE, reinterpret_cast<void**>(&symbolTable), &dummy) != 0 ||
                    bfd_read_minisymbols(hBfd, TRUE, reinterpret_cast<void**>(&symbolTable), &dummy) >= 0)
                {
                    StackframeFindData data;
                    data.symbolTable = symbolTable;
                    
                    for( USHORT i = 0; i < _count; ++i )
                    {
                        data.address = reinterpret_cast<bfd_vma>(_stackFrames[i].address);
                        data.text = NULL;
                        bfd_map_over_sections(hBfd, &s_findFunctionNameInSection, &data);
                        _stackFrames[i].text = data.text;
                    }
                    free(symbolTable);
                }
                
            }
            bfd_close(hBfd);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
